#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

#define DTSLED_NAME "redled"
#define DTSLED_CNT	1

struct dtsled_st
{
	dev_t devid;
	int major;
	int minor;
	int led_gpio;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	struct device_node *node;
};
static struct dtsled_st dtsled;

static ssize_t dtsled_read(struct file *flip, char __user *buf, size_t cnt, loff_t *off_t)
{
	return 0;
}

static ssize_t dtsled_write(struct file *flip, const char __user *buf, size_t cnt, loff_t *off_t)
{
	int status =0;
	int ret =0;
	struct dtsled_st *me = flip->private_data;

	ret = copy_from_user(&status, buf, 1);
	if(ret <0)
	{
		printk("copy_from_user error\r\n");
		return -EINVAL;
	}
	
	if(status ==0)
	{
		gpio_set_value(me->led_gpio,1);
	}
	else
	{
		gpio_set_value(me->led_gpio,0);
	}
	
	return 0;
}

static int dtsled_open(struct inode *inode, struct file *flip)
{
	flip->private_data = &dtsled;

	return 0;
}

static int dtsled_release(struct inode *inode, struct file *flip)
{
	return 0;
}

static struct file_operations dtsled_ops=
{
	.owner 		= THIS_MODULE,
	.read 		= dtsled_read,
	.write 		= dtsled_write,
	.open 		= dtsled_open,
	.release 	= dtsled_release,
};

static int __init dtsled_init(void)
{
	int ret;

	//获取设备节点
	dtsled.node = of_find_node_by_path("/leds/led2");
	if(dtsled.node ==NULL)
	{
		printk("of_find_node_by_path error\r\n");
		return -EINVAL;
	}
	printk("of_find_node_by_path sucess\r\n");

	//获取GPIO
	dtsled.led_gpio = of_get_named_gpio(dtsled.node, "gpios", 0);
	if(dtsled.led_gpio <0)
	{
		printk("of_get_named_gpio error\r\n");
		return -EINVAL;
	}
	printk("gpio:%d\r\n",dtsled.led_gpio);

	gpio_request(dtsled.led_gpio, "redled");

	ret = gpio_direction_output(dtsled.led_gpio, 1);
	if(ret <0)
	{
		printk("gpio_direction_output error\r\n");	
	}

	//申请设备号
	if(dtsled.major)
	{
		dtsled.devid = MKDEV(dtsled.major, 0);
		ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
		if(ret<0)
		{
			
		}
	}
	else
	{
		ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
		if(ret <0)
		{

		}
		dtsled.major = MAJOR(dtsled.devid);
		dtsled.minor = MINOR(dtsled.devid);
	}

	//注册字符设备
	dtsled.cdev.owner = THIS_MODULE;
	cdev_init(&dtsled.cdev, &dtsled_ops);
	ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
	if(ret <0)
	{

	}

	//创建类
	dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
	if(IS_ERR(dtsled.class))
	{
		return PTR_ERR(dtsled.class);
	}	

	//创建设备
	dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
	if(IS_ERR(dtsled.device))
	{
		return PTR_ERR(dtsled.device);
	}	

	printk("dtsled init sucess\r\n");

	return 0;
}

static void __exit dtsled_exit(void)
{
	//释放GPIO
	gpio_set_value(dtsled.led_gpio,1);
	gpio_free(dtsled.led_gpio);

	//注销字符设备
	cdev_del(&dtsled.cdev);
	unregister_chrdev_region(dtsled.devid, DTSLED_CNT);

	//注销类、设备
	device_destroy(dtsled.class, dtsled.devid);
	class_destroy(dtsled.class);

	printk("dtsled exit sucess\r\n");
}

module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jtb");

